﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Noris.Schedule.Support.Core;
using Noris.Schedule.Support.Sql;

namespace Noris.Schedule.Support.Green
{
	public partial class FiltersCls
	{
		#region SYNTÉZA DAT FILTRU
		/// <summary>
		/// Do daného selectu aplikuje filtr, specifikovaný jeho číslem.
		/// Pokud filtr nenajde, nic neaplikuje.
		/// Daný SELECT musí obsahovat zapojené tabulky master třídy.
		/// </summary>
		/// <param name="filterNumber">Číslo filtru</param>
		/// <param name="select">Inp/Out SQL SELECT</param>
        public void ApplyFilterToSelect(int filterNumber, ref string select)
        {
            ClassTree classTree = null;
            this.ApplyFilterToSelect(filterNumber, ref select, ref classTree);
        }
		/// <summary>
		/// Do daného selectu aplikuje filtr, specifikovaný jeho číslem.
		/// Pokud filtr nenajde, nic neaplikuje.
		/// Daný SELECT musí obsahovat zapojené tabulky master třídy.
		/// </summary>
		/// <param name="filterNumber">Číslo filtru</param>
		/// <param name="select">Inp/Out SQL SELECT</param>
        public void ApplyFilterToSelect(int filterNumber, ref string select, ref ClassTree classTree)
		{
			FilterInfo filterData = this[filterNumber];
			if (filterData != null)
				// Pokud najdu filtr:
                filterData.ApplyFilterToSelect(ref select, ref classTree);
            else
                Throw.AplError(MessageInfo.Get("Nelze aplikovat filtr %0 do SELECTU, filtr tohoto čísla neexistuje.", filterNumber));
		}
        /// <summary>
        /// Do daného stromu tříd aplikuje filtr a vrátí čistou filtrační frázi danou tímto filtrem
        /// </summary>
        /// <param name="filterNumber"></param>
        /// <param name="classTree"></param>
        /// <returns></returns>
        public string ApplyFilterToClassTree(int filterNumber, ClassTree classTree)
        {
            FilterInfo filterData = this[filterNumber];
            if (filterData != null)
                // Pokud najdu filtr:
                return filterData.ApplyFilterToClassTree(classTree);
            else
                Throw.AplError(MessageInfo.Get("Nelze aplikovat filtr %0 do SELECTU, filtr tohoto čísla neexistuje.", filterNumber));
            return null;
        }
        /// <summary>
		/// Vrátí SELECT s filtrem
		/// </summary>
		/// <param name="filterNumber"></param>
		/// <returns></returns>
		public string GetSelect(int filterNumber)
		{
			FilterInfo filterData = this[filterNumber];
			if (filterData == null) return null;
			
			string sqlSel = filterData.GetFilterSqlSelect();

			return sqlSel;
		}
		/// <summary>
		/// Úprava objektu = položka (filtr), který se vrací z Cache do aplikace.
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		protected override FilterInfo DataItemGetBefore(FilterInfo item)
		{
            if (item == null) return null;
			return item.GetClone();
		}
		#endregion
	}
	#region EXTENZE DATOVÝCH TŘÍD - ROZŠÍŘENÍ O DALŠÍ FUNKCE
	#region CLASS FilterInfo : jeden konkrétní filtr
	/// <summary>
	/// Jeden konkrétní filtr
	/// </summary>
	public partial class FilterInfo : RepositorySegmentItem<Int32>, IDbRecord
	{
		#region KONSTRUKCE STROMU TŘÍD
		/// <summary>
		/// Vrátí strom tříd příslušný k tomuto filtru.
		/// Strom tříd dokáže najít tabulku podle názvu atributu a čísla entity, vygenerovat alias a zajistit joinování použitých tabulek.
		/// </summary>
		/// <returns></returns>
		internal NorisClassTree GetNorisClassTree()
		{
			return GetNorisClassTree(null);
		}
		/// <summary>
		/// Vrátí strom tříd příslušný k tomuto filtru.
		/// Strom tříd dokáže najít tabulku podle názvu atributu a čísla entity, vygenerovat alias a zajistit joinování použitých tabulek.
		/// </summary>
		/// <returns></returns>
		internal NorisClassTree GetNorisClassTree(SqlParsePhraseFrom phraseFrom)
		{
			string prefixAlias = "Fl" + this.CisloFiltru.ToString().Replace("-", "");
			ClassInfo classData = this.SourceClassData;
			if (classData == null) return null;

			NorisClassTree classTree = NorisClassTree.Create(classData, this.EntityIList, prefixAlias, phraseFrom, MessageInfo.Get("Chyba ve filtru %0", this.ToString()));

			return classTree;
		}
		/// <summary>
		/// Vrátí klon sebe sama.
		/// Klonuje hodnoty základních property, vytváří kopii _EntityList, _EntLocker, _ItemList, _ItmLocker
		/// </summary>
		/// <returns></returns>
		public FilterInfo GetClone()
		{
			FilterInfo clone = (FilterInfo)this.MemberwiseClone();
			clone._EntityList = new List<FilterEntityCls>(this._GetEntityList());   // Vrátí (zajistí načtení do this = v cache) dat EntityList
			clone._EntLocker = new object();
			clone._ItemList = new LinkedList<FilterItemCls>(this._GetItemList());   // Vrátí (zajistí načtení do this = v cache) dat ItemList
			clone._ItmLocker = new object();
			return clone;
		}
		#endregion
		#region PODPORA PRO RUČNÍ VYTVÁŘENÍ FILTRU A INTERAKTIVNÍ MODIFIKACI
		/// <summary>
		/// Vytvoří prázdný filtr pro danou třídu.
		/// Filtr neobsahuje žádné podmínky, obsahuje pouze root entitu.
		/// </summary>
		/// <param name="classNumber"></param>
		/// <returns></returns>
		public static FilterInfo CreateForClass(int classNumber)
		{
			FilterInfo filterInfo = new FilterInfo();
			filterInfo._cislo_tridy = classNumber;
			filterInfo._celou_tridu = "A";

			FilterEntityCls rootEntity = FilterEntityCls.CreateRootForClass(classNumber);
			filterInfo._EntityList = new List<FilterEntityCls>();
			filterInfo._EntityList.Add(rootEntity);

			filterInfo.InnerFilterIsParenthesised = false;

			return filterInfo;
		}
		/// <summary>
		/// Uzavře filtr do závorek, pokud je to zapotřebí
		/// </summary>
		public void ParenthesiseFilterIfNeed()
		{
			LinkedList<FilterItemCls> itemList = this._GetItemList();
			if (itemList.Count < 1) return;
			if (!this.IsFilterParenthesized)
				this.ParenthesiseFilter();
		}
		/// <summary>
		/// Vrací true, pokud je celý filtr obalen závorkami.
		/// Pokud není ve filtru žádný výraz, vrací false.
		/// Pokud je ve filtru 1 výraz, vrací true, pokud má na začátku i na konci závorky : (expression).
		/// Pokud je ve filtru více výrazů, vrací true pokud jsou závorky před prvním výrazem, a nikde uprostřed nejsou přerušeny.
		/// Například pro filtr "(e1 and (e2 or e3) and (e4))" vrací true.
		/// Například pro filtr "(e1 and e2) or (e3 and (e4))" vrací false.
		/// </summary>
		public bool IsFilterParenthesized
		{
			get
			{
				LinkedList<FilterItemCls> itemList = this._GetItemList();
				if (itemList.Count == 0) return false;

				int parenthesisCount = 0;
				LinkedListNode<FilterItemCls> item = itemList.First;
				while (item != null)
				{
					FilterItemCls filterItem = item.Value;

					// Přičtu závorky vlevo, odečtu závorky vpravo:
					parenthesisCount += filterItem.LeftBracesEx.Length - filterItem.RightBracesEx.Length;

					// Pokud za tímto výrazem končí závorky, ale výraz pokračuje dalším slovem, vrátím false:
					if (parenthesisCount <= 0 && item.Next != null) return false;

					item = item.Next;
				}
				return true;
			}
		}
		/// <summary>
		/// Zajistí uzavření aktuálního stavu filtru do závorky.
		/// Přidá levou závorku na začátek prvního výrazu, a pravou závorku na konec posledního výrazu.
		/// </summary>
		public void ParenthesiseFilter()
		{
			LinkedList<FilterItemCls> itemList = this._GetItemList();
			if (itemList.Count > 0)
			{
				itemList.First.Value.ParenthesiseLeft();
				itemList.Last.Value.ParenthesiseRight();
			}
		}
		/// <summary>
		/// Tato metoda zajistí uzavření interního filtru do závorek.
		/// Interní filtr je ten načtený z databáze.
		/// Ten by se měl do závorek uzavřít před začátkem přidávání interaktivních položek.
		/// </summary>
		protected void ParenthesiseInnerFilter()
		{
			if (!this.InnerFilterIsParenthesised)
			{
				this.ParenthesiseFilterIfNeed();
				this.InnerFilterIsParenthesised = true;
			}
		}
		/// <summary>
		/// Obsahuje true poté, kdy byl vnitřní filtr uzavřen do závorek.
		/// Obsahuje false před tím.
		/// </summary>
		protected bool InnerFilterIsParenthesised { get; set; }
		/// <summary>
		/// Přidá další výraz k filtru, na začátek filtru.
		/// </summary>
		/// <param name="entityNumber">Číslo entity, na kterou se výraz vztahuje. Základní entita = 1.</param>
		/// <param name="fullDbColumn">Plný název DB columnu, ke kterému se vztahuje tento výraz. Typicky: "lcs.sk_hlavicka.typ"</param>
		/// <param name="filterExpression">Výraz pro filtrování. 
		/// Měl by obsahovat identicky uvedený sloupec, jako v parametru fullDbColumn. 
		/// Například: "lcs.sk_hlavicka.typ = '2'".
		/// Nelze se odkazovat na jiné db sloupce ani tabulky, než na tento konkrétní.
		/// Pokud je třeba se ve výrazu odkázat se na jiný sloupec téže tabulky, použije se jako alias tento text: "{{Alias}}."
		/// Filtr dosadí konkrétní alias, takže ve výsledku bude například "subj."
		/// Pokud je tedy filtr sestaven nad třídou Výrobní příkaz, pak lze přidat výraz takový:
		/// fullDbColumn = "lcs.product_order.cislo_subjektu" (tabulka musí existovat),
		/// a pak filterExpression nemá být "lcs.product_order.status = 'A' and lcs.product_order.qty > 25 and lcs.product_order.constr_element = 12345",
		/// ale správně má být: "{{Alias}}.status = 'A' and {{Alias}}.qty > 25 and {{Alias}}.constr_element = 12345".
		/// Pokud bude použito alias například "po", pak ve výsledku bude výraz takový: "po.status = 'A' and po.qty > 25 and po.constr_element = 12345"
		/// Výsledkem výrazu má být SQL podmínka, vracející true / false.</param>
		/// <param name="joinToNext">Spojítko tohoto výrazu na další pokračování filtru (typicky AND)</param>
		public void AddExpressionToBegin(int entityNumber, string fullDbColumn, string filterExpression, string joinToNext)
		{
			this.ParenthesiseInnerFilter();
			LinkedList<FilterItemCls> itemList = this._GetItemList();
			itemList.AddFirst(FilterItemCls.CreateForExpression(this, entityNumber, fullDbColumn, filterExpression, joinToNext));
		}
		/// <summary>
		/// Přidá další výraz k filtru, na konec filtru.
		/// </summary>
		/// <param name="joinToPrev">Spojítko tohoto výrazu (typicky AND) a stávajícího filtru</param>
		/// <param name="entityNumber">Číslo entity, na kterou se výraz vztahuje. Základní entita = 1.</param>
		/// <param name="fullDbColumn">Plný název DB columnu, ke kterému se vztahuje tento výraz. Typicky: "lcs.sk_hlavicka.typ"</param>
		/// <param name="filterExpression">Výraz pro filtrování. 
		/// Měl by obsahovat identicky uvedený sloupec, jako v parametru fullDbColumn. 
		/// Například: "lcs.sk_hlavicka.typ = '2'".
		/// Nelze se odkazovat na jiné db sloupce ani tabulky, než na tento konkrétní.
		/// Pokud je třeba se ve výrazu odkázat se na jiný sloupec téže tabulky, použije se jako alias tento text: "{{Alias}}."
		/// Filtr dosadí konkrétní alias, takže ve výsledku bude například "subj."
		/// Pokud je tedy filtr sestaven nad třídou Výrobní příkaz, pak lze přidat výraz takový:
		/// fullDbColumn = "lcs.product_order.cislo_subjektu" (tabulka musí existovat),
		/// a pak filterExpression nemá být "lcs.product_order.status = 'A' and lcs.product_order.qty > 25 and lcs.product_order.constr_element = 12345",
		/// ale správně má být: "{{Alias}}.status = 'A' and {{Alias}}.qty > 25 and {{Alias}}.constr_element = 12345".
		/// Pokud bude použito alias například "po", pak ve výsledku bude výraz takový: "po.status = 'A' and po.qty > 25 and po.constr_element = 12345"
		/// Výsledkem výrazu má být SQL podmínka, vracející true / false.</param>
		public void AddExpressionToEnd(string joinToPrev, int entityNumber, string fullDbColumn, string filterExpression)
		{
			this.ParenthesiseInnerFilter();
			LinkedList<FilterItemCls> itemList = this._GetItemList();
			if (itemList.Count > 0)
				itemList.Last.Value.SetSpojitko(joinToPrev);
			itemList.AddLast(FilterItemCls.CreateForExpression(this, entityNumber, fullDbColumn, filterExpression, ""));
		}
		#endregion
		#region APLIKACE FILTRU DO SQL PŘÍKAZU
		/// <summary>
		/// Aplikuje this filtr do daného SQL příkazu, do prvního SQL SELECTU který v něm najde.
		/// </summary>
		/// <param name="sql"></param>
        public void ApplyFilterToSelect(ref string select)
        {
            ClassTree classTree = null;
            this.ApplyFilterToSelect(ref select, ref classTree);
        }
        /// <summary>
		/// Aplikuje this filtr do daného SQL příkazu, do prvního SQL SELECTU který v něm najde.
		/// </summary>
		/// <param name="sql"></param>
        public void ApplyFilterToSelect(ref string select, ref ClassTree classTree)
		{
			using (Noris.Schedule.Support.Sql.SqlText parser = SqlText.CreateFromText(select))
			{	// Rozparsuju daný SQL text na položky a z nich sestavím SQL příkazy:
				SqlParseCommand sqlCommand = parser.FindFirstCommand("SELECT");
				if (sqlCommand != null)
				{	// Pokud daný text obsahuje SQL příkaz SELECT, pak nechám filtr (this), aby se do něj promítnul:
                    this.ApplyFilterToSqlCommand(sqlCommand, ref classTree);
					select = parser.OutputSqlText;
				}
			}
		}
        /// <summary>
        /// Aplikuje svoje filtrační podmínky a navázané třídy do daného SqlCommandu.
        /// SqlCommand by měl implementovat ISqlParseFrom = měl by obsahovat property PhraseFrom, 
        /// kde je parsovaná fráze FROM tohoto příkazu.
        /// Filtr tuto frázi používá pro vyhledání tabulek své master třídy.
        /// Pokud v daném SQL příkazu jsou tabulky master třídy použity, filtr je použije. 
        /// Pokud nejsou použity, filtr je přijoinuje, a to na některou jinou již existující tabulku master třídy filtru.
        /// </summary>
        /// <param name="sqlCommand">Rozparsovaný SQL příkaz</param>
        public void ApplyFilterToSqlCommand(SqlParseCommand sqlCommand)
        {
            ClassTree classTree = null;
            this.ApplyFilterToSqlCommand(sqlCommand, ref classTree);
        }
        /// <summary>
		/// Aplikuje svoje filtrační podmínky a navázané třídy do daného SqlCommandu.
		/// SqlCommand by měl implementovat ISqlParseFrom = měl by obsahovat property PhraseFrom, 
		/// kde je parsovaná fráze FROM tohoto příkazu.
		/// Filtr tuto frázi používá pro vyhledání tabulek své master třídy.
		/// Pokud v daném SQL příkazu jsou tabulky master třídy použity, filtr je použije. 
		/// Pokud nejsou použity, filtr je přijoinuje, a to na některou jinou již existující tabulku master třídy filtru.
		/// </summary>
		/// <param name="sqlCommand">Rozparsovaný SQL příkaz</param>
        public void ApplyFilterToSqlCommand(SqlParseCommand sqlCommand, ref ClassTree classTree)
		{
            this._ApplyFilterToSqlCommand(sqlCommand, ref classTree);
        }
        /// <summary>
        /// Aplikuje svůj filtr do daného stromu tříd, vrátí filtrační výraz který je ve shodě se stromem tříd
        /// </summary>
        /// <param name="classTree"></param>
        /// <returns></returns>
        public string ApplyFilterToClassTree(ClassTree classTree)
        {
            return this._ApplyFilterToClassTree(classTree);
        }
        /// <summary>
        /// Aplikuje svůj filtr do daného SQL COMMANDU select
        /// </summary>
        /// <param name="sqlCommand"></param>
        /// <param name="classTree"></param>
        private void _ApplyFilterToSqlCommand(SqlParseCommand sqlCommand, ref ClassTree classTree)
        {
            // Získám SQL příkaz s frází FROM:
			if (!(sqlCommand is ISqlCmdWithFrom))
				Throw.SysError(MessageInfo.Get("Pro aplikaci filtru do příkazu musí tento příkaz podporovat parsování fráze FROM (ISqlCmdWithFrom). Daná třída %0 to nepodporuje.", sqlCommand.GetType().Name));

			ISqlCmdWithFrom cmd = sqlCommand as ISqlCmdWithFrom;
			if (cmd == null)
				Throw.SysError(MessageInfo.Get("Pro aplikaci filtru do příkazu musí být předán existující SQL příkaz, ale nyní byl předán NULL."));

            // Linkovaný seznam položek filtru:
            LinkedList<FilterItemCls> itemList = this.ItemList;
            if (itemList.Count == 0) return;                    // Pokud filtr nemá žádné položky (existuje jen formálně), pak končím ihned: SELECT se vůbec nebude modifikovat.

            // Získám strom tříd mého filtru, který je opřený o existující frázi FROM (pouze pokud ClassTree není předán):
            if (classTree == null)
            {
                classTree = new ClassTree(cmd.PhraseFrom);      // Do ref proměnné mohu vytvořit new objekt, a vrátí se ven.
                classTree.AliasPrefix = "f" + this.CisloFiltru.ToString();
            }
            // Pokud je ClassTree předán, je to zodpovědnost uživatele, aby strom tříd korektně uschoval a používal pro shodný select.
            // Pak jako bonus bude výsledný SELECT optimálně vytvořen v případě, kdy do SELECTU bude aplikováno postupně více různých filtrů.

            // Kompletní práce s aplikací 
            string where = this._ApplyFilterToClassTree(classTree);

            string from = classTree.AddedTables;                // sekvence JOINŮ, je zakončeno EOL, bez mezery.

			// Do selectu aplikujeme DISTINCT, FROM, WHERE:
			sqlCommand.AddKeyWord("SELECT", "DISTINCT", "SELECT");
            if (classTree.IsJoinToFromPhrase || !from.StartsWith("FROM ", StringComparison.OrdinalIgnoreCase))
            {   // Strom tříd je opřený o frázi FROM => k frázi FROM přidáme zdejší tabulky, ale původní ponecháme:
                sqlCommand.AddSyntaxExpression("FROM", from, true);
            }
            else
            {   // Strom tříd vyrostl bez opory fráze FROM, takže do FROM dáme výhradně naše tabulky:
                SqlParseSyntaxItem syntaxFrom = sqlCommand.SyntaxFind("FROM");
                syntaxFrom.Deactivate();
                syntaxFrom.AddSyntaxExpression(from.Substring(5));
            }
            sqlCommand.AddSyntaxExpression("WHERE", where, true);
		}
        /// <summary>
        /// Fyzicky aplikuje sebe jako filtr do stromu tříd, a souběžně sestavuje odpovídající filtrační výraz
        /// </summary>
        /// <param name="classTree"></param>
        /// <returns></returns>
        private string _ApplyFilterToClassTree(ClassTree classTree)
        {
            LinkedList<FilterItemCls> itemList = this.ItemList;
            if (itemList.Count == 0) return "";                    // Pokud filtr nemá žádné položky (existuje jen formálně), pak končím ihned: SELECT se vůbec nebude modifikovat.
            
            string where = "";
            int parenthesisCount = 0;
            LinkedListNode<FilterItemCls> item = itemList.First;
            while (item != null)
            {
                // Zajistit zatřídění zdroje dat pro tento prvek do stromu tříd (do konstrukce FROM + JOIN).
                FilterItemCls filterItem = item.Value;
                string entityTreePath = this.GetEntityTreePath(filterItem);

                // Určit korektně aliasovaný název sloupce / výraz:
                string itemDataSource = null;
                if (filterItem.IsExpression)
                {   // Pokud aktuální položka filtru je Výraz, pak je třeba zajistit aliasování každého sloupce zvlášť:
                    itemDataSource = Parser.Parser.ChangeTextInExpression(filterItem.Expression, text => this.GetAliasedColumn(classTree, entityTreePath, filterItem, text));
                }
                else
                {   // Není to výraz, vytvořím aliasovaný název sloupce:
                    itemDataSource = this.GetAliasedColumn(classTree, entityTreePath, filterItem, filterItem.ColumnReal);
                }

                // Filtrační výraz, včetně závorek a spojítka (AND/OR):
                if (itemDataSource != null)
                    where += filterItem.GetFilterExpression(itemDataSource, ref parenthesisCount, (item.Next == null));

                // Další položka:
                item = item.Next;
            }
            return where;
        }
        /// <summary>
        /// Metoda sestaví a vrátí formální cestu od root třídy k entitě dané položky filtru.
        /// Metoda v cestě nevrací sloupec nebo výraz položky filtru, pouze cestu k dané třídě skrz strom tříd.
        /// </summary>
        /// <param name="filterItem"></param>
        /// <returns></returns>
        private string GetEntityTreePath(FilterItemCls filterItem)
        {
            string treePath = "";
            Dictionary<int, FilterEntityCls> entityDict = this.EntityDict;
            FilterEntityCls entity = entityDict[filterItem.EntityNumber];

            // Strom tříd:
            while (entity != null)
            {
                if (entity.EntityNumber <= 1)
                {   // Root entita:
                    int classNumber = this.CisloTridy;
                    treePath = ClassTree.TREEPATH_CLASS + classNumber.ToString() + ClassTree.TREEPATH_SEP_ITEM.ToString() + treePath;
                    break;
                }
                else
                {   // Relation entita:
                    //  Workaroundy pro Green, který má u netypových vztahů "přechodník" přes obecný subjekt:
                    // 1. zde jdeme od konce stromu = od Up entity
                    // 2. jdeme směrem k Parent entitě (Root)
                    // 3. u netypových vztahů v řetězu entit nejprve najdeme vztah určitého čísla a směru, a konkrétní třídu
                    // 4. jako Parent entita se najde tentýž vztah a strana vztahu, ale třída bude 13 = Obecný subjekt
                    //     => entitu popsanou pod bodem 4 přeskočíme, protože ClassTree tento krok nepotřebuje.
                    // 5. naopak pokud najdeme vztah ze třídy 13 přímo, a je poslední, 
                    //     => pak do treePath nevkládáme třídu (klíč "C13"), protože ClassTree to umí i bez toho.
                    int relationNumber = entity.CisloVztahu;
                    string relationStrana = entity.Strana;
                    string relationSide = (relationStrana == "R" ? ClassTree.TREEPATH_REL_RIGHT_TO_LEFT.ToString() : ClassTree.TREEPATH_REL_LEFT_TO_RIGHT.ToString());
                    int classNumber = entity.CisloTridy;

                    string key = relationSide + relationNumber.ToString() + ClassTree.TREEPATH_SEP_ITEM.ToString();
                    // if (classNumber != 13)
                    key += "C" + classNumber.ToString() + ClassTree.TREEPATH_SEP_ITEM.ToString();
                    treePath = key + treePath;

                    if (entity.HasParentEntity)
                    {
                        entity = entityDict[entity.ParentEntityNumber];
                        if (entity.CisloVztahu == relationNumber && entity.Strana == relationStrana && classNumber != 13 && entity.CisloTridy == 13)
                        {   // Nynější entita reprezentuje netypový vztah, který můžeme přeskočit:
                            if (entity.HasParentEntity)
                                entity = entityDict[entity.ParentEntityNumber];
                            else
                                entity = null;
                        }
                    }
                    else
                    {
                        entity = null;
                    }
                }
            }

            return treePath;
        }
		/// <summary>
        /// Metoda se pokusí najít zadaný text fullColumn jako plný název sloupce (owner.table.column) v datové třídě, kterou navazuje entita filtračního výrazu.
        /// Poznámka: detekuje se pouze, zda z textu lze vyčlenit název tabulky, který v dané třídě existuje, a zda holý sloupec je neprázdný. Netestuje se fyzická existence sloupce v tabulce.
        /// Pokud se nepodaří tabulku najít, vrací se null.
        /// Pokud třída takovou tabulku obsahuje, pak se sestaví plná formální cesta k sloupci (za pomoci ClassTree) a vrátí se vstupní sloupec aliasovaný podle odpovídající tabulky zapojené do ClassTree.
		/// </summary>
        /// <param name="entityTreePath">Formální cesta skrz strom tříd od Root třídy ke třídě, v níž se nachází daný filterItem</param>
        /// <param name="filterItem">Položka filtru</param>
        /// <param name="fullColumn">Plný název db sloupce, nebo možná cokoliv jiného (metoda se volá i pro aliasování uvnitř výrazu), je třeba prověřit zda daný string se skutečně vztahuje ke třídě položky filtru.</param>
        /// <returns></returns>
        private string GetAliasedColumn(ClassTree classTree, string entityTreePath, FilterItemCls filterItem, string fullColumn)
		{
            // To, zda text v parametru fullColumn vyjadřuje fullname column naší třídy, prověří naše třída:
            FilterEntityCls entity = this.EntityDict[filterItem.EntityNumber];
            ClassTableType tableType;
            string dbColumn;
            if (!entity.ClassData.TryParseFullDbColumn(fullColumn, out tableType, out dbColumn))
                // zadaný výraz neobsahuje tabulku z naší třídy, vracíme false:
                return null;

            string columnTreePath = ClassTree.ConvertTableType(tableType) + ClassTree.TREEPATH_SEP_ATTR_COLUMN.ToString() + dbColumn;
            string fullTreePath = entityTreePath + columnTreePath;

            IClassTreeColumn column = classTree.AddBranchToTree(fullTreePath);       // Filtrační sloupec nepřidávám do FinalColumns, tam bývají sloupce do SELECT fields.
            if (column == null) return null;
            string aliasedColumn = column.Column;
            return aliasedColumn;
        }
        /// <summary>
        /// Aplikuje svoje filtrační podmínky a navázané třídy do daného SqlCommandu.
        /// SqlCommand by měl implementovat ISqlParseFrom = měl by obsahovat property PhraseFrom, 
        /// kde je parsovaná fráze FROM tohoto příkazu.
        /// Filtr tuto frázi používá pro vyhledání tabulek své master třídy.
        /// Pokud v daném SQL příkazu jsou tabulky master třídy použity, filtr je použije. 
        /// Pokud nejsou použity, filtr je přijoinuje, a to na některou jinou již existující tabulku master třídy filtru.
        /// </summary>
        /// <param name="sqlCommand">Rozparsovaný SQL příkaz</param>
        public void ApplyFilterToSqlCommand_ViaOldNorisClassTree(SqlParseCommand sqlCommand)
        {
            // Získám SQL příkaz s frází FROM:
            if (!(sqlCommand is ISqlCmdWithFrom))
                Throw.SysError(MessageInfo.Get("Pro aplikaci filtru do příkazu musí tento příkaz podporovat parsování fráze FROM (ISqlCmdWithFrom). Daná třída %0 to nepodporuje.", sqlCommand.GetType().Name));

            ISqlCmdWithFrom cmd = sqlCommand as ISqlCmdWithFrom;
            if (cmd == null)
                Throw.SysError(MessageInfo.Get("Pro aplikaci filtru do příkazu musí být předán existující SQL příkaz, ale nyní byl předán NULL."));

            // Získám strom tříd mého filtru, který je opřený o existující frázi FROM:
            NorisClassTree classTree = this.GetNorisClassTree(cmd.PhraseFrom);
            LinkedList<FilterItemCls> itemList = this.ItemList;
            string whereExpr = "";
            int parenthesisCount = 0;
            LinkedListNode<FilterItemCls> item = itemList.First;
            while (item != null)
            {
                // Zajistit zatřídění zdroje dat pro tento prvek do stromu tříd (do konstrukce FROM + JOIN).
                // Určit tím alias zdrojové tabulky pro daný sloupec:
                string itemDataSource = classTree.GetEntryColumnSource((INorisClassTreeEntry)item.Value);

                // Filtrační výraz, včetně závorek a spojítka (AND/OR):
                whereExpr += item.Value.GetFilterExpression(itemDataSource, ref parenthesisCount, (item.Next == null));

                // Další položka:
                item = item.Next;
            }

            string from = classTree.GetUsedTableList();       // sekvence JOINŮ, je zakončeno EOL, bez mezery.

            // Do selectu aplikujeme DISTINCT, FROM, WHERE:
            sqlCommand.AddKeyWord("SELECT", "DISTINCT", "SELECT");
            sqlCommand.AddSyntaxExpression("FROM", from, true);
            sqlCommand.AddSyntaxExpression("WHERE", whereExpr, true);
		}
		#endregion
		#region SESTAVENÍ SELECTU PODLE FILTRU
		/// <summary>
		/// Vrátí SQL select pro načtení vět odpovídajících tomuto filtru
		/// </summary>
		/// <returns></returns>
		internal string GetFilterSqlSelect()
		{
			NorisClassTree classTree = this.GetNorisClassTree();
			LinkedList<FilterItemCls> itemList = this.ItemList;
			List<string> fieldList = new List<string>();
			int parenthesisCount = 0;
			LinkedListNode<FilterItemCls> item = itemList.First;
			string whereExpr = "";
			while (item != null)
			{
				// Alias sloupce, řeší zařazení tabulky ze stromu tříd do konstrukce FROM + JOIN:
				string itemDataSource = classTree.GetEntryColumnSource((INorisClassTreeEntry)item.Value);
				fieldList.Add(itemDataSource);

				// Filtrační výraz, včetně závorek a spojítka (AND/OR):
				whereExpr += item.Value.GetFilterExpression(itemDataSource, ref parenthesisCount, (item.Next == null));
				
				// Další položka:
				item = item.Next;
			}

			string fields = Library.ListToString<string>(fieldList, ",");
			string from = classTree.GetUsedTableList();       // je zakončeno EOL, bez mezery.
			return "SELECT " + fields + Environment.NewLine + from + "WHERE " + whereExpr + Environment.NewLine;
		}
		#endregion
    }
	#endregion
	#region CLASS FilterItemCls : Jedna položka filtru, generování výrazu pro filtr
	/// <summary>
	/// FilterItemCls : Tato tabulka (lcs.nis_filters) není navázaná k žádné Noris třídě.
	/// Data načtena z tabulky lcs.nis_filters
	/// </summary>
	public partial class FilterItemCls : Noris.Schedule.Support.Sql.IDbRecord, INorisClassTreeEntry
	{
		#region METODY
		/// <summary>
		/// Metoda vrátí filtrační výraz této položky filtru.
        /// K datovému zdroji (column/výraz) připojí operátor rovnosti a cílovou hodnotu, obalí závorkami a koncovým spojítkem (and/or).
		/// </summary>
		/// <param name="itemDataSource">Zdroj dat této položky, buď databázový sloupec, s již konkrétním aliasem tabulky, anebo výraz (taky korektně aliasovaný).</param>
		/// <param name="parenthesisCount">Počet otevřených závorek In:před / Out:po tomto výrazu</param>
		/// <param name="isLastItem">Příznak, že tento prvek je posledním prvkem, nebude za ním AND ani OR</param>
		/// <returns></returns>
		internal string GetFilterExpression(string itemDataSource, ref int parenthesisCount, bool isLastItem)
		{
			string parPre = this.LeftBracesEx;
			parenthesisCount += parPre.Length;                       // Počet aktivních závorek před tímto výrazem

			string filterExpr = this._GetFiltExpr(itemDataSource);   // Sestavíme vlastní filtrační výraz, bez závorek, bez spojítek (doplní se filtrační komparátor a požadovaná hodnota)

			string parAft = this.RightBracesEx;
			if (parAft.Length > parenthesisCount)
				Throw.SysError(MessageInfo.Get("Chyba v definici filtru %0, počet koncových závorek na položce %1 je větší (%2), než počet otevřených závorek (%3)", this.Parent.ReferNazev, this.ReferNazev, parAft.Length, parenthesisCount));
			parenthesisCount -= parAft.Length;                       // Počet aktivních závorek po tomto výrazu

			// Ozávorkovat výraz:
			string result = parPre + filterExpr + parAft;            // ((( výraz ))

			// Přidat AND/OR nebo dokončit závorky:
			if (isLastItem)
			{	// Poslední část výrazu: nebude AND/OR, ale dorovnám závorky
				if (parenthesisCount > 0)
				{
					parAft = "".PadRight(parenthesisCount, ')');
					result += parAft;
					parenthesisCount -= parAft.Length;
				}
			}
			else
			{	// Přidám AND/OR:
				result += this.SpojitkoEx;
			}

			return result;
		}
		/// <summary>
		/// Vrátí filtrační výraz, výhradně vlastní výraz (neřeší závorky kolem výrazu, a ani AND/OR na konci).
		/// </summary>
        /// <param name="itemDataSource">Zdroj dat této položky, buď databázový sloupec, s již konkrétním aliasem tabulky, anebo výraz (taky korektně aliasovaný).</param>
		/// <returns></returns>
		private string _GetFiltExpr(string itemDataSource)
		{
			// Podle podmínky typu rozdělit:
			switch (this.DatatypEx)
			{
				case FilterDataType.Boolean:
					return itemDataSource;
				case FilterDataType.CkSaldoTyp:
					Throw.SysError(MessageInfo.Get("Objekt FilterItem dosud nepracuje s položkami filtru typu CkSaldo."));
					break;
				case FilterDataType.Date:
				case FilterDataType.DateTime:
				case FilterDataType.Number:
				case FilterDataType.Varchar:
				case FilterDataType.FolderChar:
				case FilterDataType.Folder:
				case FilterDataType.Pointer:
					return _GetFiltExprValue(itemDataSource, this.ValueEx);
				case FilterDataType.Kategorie:
					Throw.SysError(MessageInfo.Get("Objekt FilterItem dosud nepracuje s položkami filtru typu Kategorie."));
					break;
				default:
					return "";
			}
			return "";
		}
		/// <summary>
		/// Vrátí daný výraz, v němž nahradí full db columnname odpovídajícím aliasem.
		/// </summary>
		/// <param name="expression"></param>
		/// <param name="columnAlias"></param>
		/// <param name="entity"></param>
		/// <param name="table"></param>
		/// <returns></returns>
		private string _GetFiltExprAliased(string expression, string columnAlias, EntityTableGroup entity, EntityTableOne table)
		{
            // qqq;
			string result = expression.Replace(this._column_real, columnAlias);   // Aliasovat konkrétní db sloupec
			if (result.Contains("{{Alias}}."))
				result = result.Replace("{{Alias}}.", table.Alias + ".");         // Aliasovat current tabulku
			return result;
		}
		/// <summary>
		/// Vrátí filtrační výraz, výhradně vlastní výraz (neřeší závorky kolem výrazu, a ani AND/OR na konci).
		/// Pracuje pro datové typy date, datetime, number, varchar.
		/// </summary>
		/// <param name="expression"></param>
		/// <returns></returns>
		private string _GetFiltExprValue(string expression, string valueEx)
		{
			FilterRelType relace = this.RelaceEx;
			switch (relace)
			{
				case FilterRelType.Equal:
					return expression + " = " + valueEx;
				case FilterRelType.Like:
					return expression + " like " + valueEx;
				case FilterRelType.NotLike:
					return expression + " not like " + valueEx;
				case FilterRelType.LessThan:
					return expression + " < " + valueEx;
				case FilterRelType.GreatThan:
					return expression + " > " + valueEx;
				case FilterRelType.NotEqual:
					return expression + " <> " + valueEx;
				case FilterRelType.LessOrEqual:
					return expression + " <= " + valueEx;
				case FilterRelType.GreatOrEqual:
					return expression + " >= " + valueEx;
				case FilterRelType.IsNotNull:
					return expression + " is not null";
				case FilterRelType.IsNull:
					return expression + " is null";
				case FilterRelType.Between:
					return expression + " between " + this.Value1Ex + " and " + this.Value2Ex;
				case FilterRelType.NotExists:
					Throw.SysError(MessageInfo.Get("Aktuální verze nezpracuje filtr, v němž je použit operátor NEROZLIŠENO."));
					break;
				case FilterRelType.In:
				case FilterRelType.NotIn:
					Throw.SysError(MessageInfo.Get("Aktuální verze nezpracuje filtr, v němž je použit operátor JE/NENÍ VE SKUPINĚ."));
					break;
				case FilterRelType.In2:
					return expression + " in (" + valueEx + ")";
				case FilterRelType.NotIn2:
					return expression + " not in (" + valueEx + ")";
				case FilterRelType.Lock:
					return expression;
			}
			return "";
		}
		#endregion
		#region EXTENDED PROPERTY
		/// <summary>
		/// Parent objekt položky filtru
		/// </summary>
		public FilterInfo Parent
		{
			get
			{
				if (this.HasParent)
					return this._ParentWr.Target as FilterInfo;
				return null;
			}
			internal set
			{
				if (value == null)
					this._ParentWr = null;
				else
					this._ParentWr = new WeakReference(value);
			}
		}
		/// <summary>
		/// true pokud existuje reference na Parent filtr
		/// </summary>
		public bool HasParent
		{ get { return this._ParentWr != null && this._ParentWr.IsAlive && this._ParentWr.Target is FilterInfo; }  }
		/// <summary>
		/// WeakReference na objekt Parent, což je filtr, do kterého patřím.
		/// </summary>
		private WeakReference _ParentWr;
		/// <summary>
		/// Levé závorky, a nic jiného (i kdyby v this.LeftBraces bylo cokoli víc, zůstane zde jen soupis znaků "(" ).
		/// </summary>
		public string LeftBracesEx { get { return this._GetOnly(this.LeftBraces, "("); } }
		/// <summary>
		/// Pravé závorky, a nic jiného (i kdyby v this.RightBraces bylo cokoli víc, zůstane zde jen soupis znaků ")" ).
		/// </summary>
		public string RightBracesEx { get { return this._GetOnly(this.RightBraces, ")"); } }
		/// <summary>
		/// Vrací spojítko " AND " / " OR "
		/// </summary>
		public string SpojitkoEx { get { return this._GetSpojitkoEx(); } }
		/// <summary>
		/// Vrací typově přesný vztah podle hodnoty string načtené z definice (this.Relace)
		/// </summary>
		public FilterRelType RelaceEx { get { return _GetRelaceEx(this.Relace); } }
		/// <summary>
		/// Vrací true, pokud v tomto výrazu je uvedeno číslo pořadače jako text.
		/// To je v případě chybného zadání v repozitory, kdy vztah na pořadač je falešný, a pořadač je veden jako atribut.
		/// Uživatel pak musí vepsat číslo pořadače, a ukládá se jako text do atributu HodnotaVis.
		/// </summary>
		public bool IsCharFolder { get { return this.ColumnReal.ToLower().Trim().EndsWith(".cislo_poradace") && this.Datatyp == "varchar" && this._GetWithout(this.HodnotaVis.Trim(), "0123456789").Length == 0; } }
		/// <summary>
		/// Vrací datový typ.
		/// Pozor: pokud je uveden falešný vztah na pořadač, vrací se hodnota FilterDataType.FolderChar.
		/// </summary>
		public FilterDataType DatatypEx { get { return this._GetDatatypEx(); } }
		/// <summary>
		/// Vrací hodnotu this.HodnotaVis2 (pokud je zadaná), nebo this.HodnotaVis (to když this.HodnotaVis2 není zadaná).
		/// </summary>
		public string HodnotaVis21 { get { return (String.IsNullOrEmpty(this.HodnotaVis2) ? this.HodnotaVis : this.HodnotaVis2); } }
		/// <summary>
		/// Vrací hodnotu filtračního kritéria (jako string) za tuto položku filtru.
		/// Používá se v podmínkách, které akceptují jednu hodnotu (rovno, nerovno, menší, větší, ...).
		/// Ne v podmínkách s dvěma hodnotami (between).
		/// </summary>
		public string ValueEx { get { return _GetValueEx(this.HodnotaVis21); } }
		/// <summary>
		/// Vrací hodnotu filtračního kritéria (jako string) za tuto položku filtru.
		/// Používá se v podmínkách, které akceptují dvě hodnoty (between), jako první hodnota.
		/// </summary>
		public string Value1Ex { get { return _GetValueEx(this.HodnotaVis); } }
		/// <summary>
		/// Vrací hodnotu filtračního kritéria (jako string) za tuto položku filtru.
		/// Používá se v podmínkách, které akceptují dvě hodnoty (between), jako druhá hodnota.
		/// </summary>
		public string Value2Ex { get { return _GetValueEx(this.HodnotaVis2); } }

		// private
		/// <summary>
		/// Vrátí spojítko " AND " / " OR "
		/// </summary>
		/// <returns></returns>
		private string _GetSpojitkoEx()
		{
			string spojitko = this.Spojitko;
			if (String.IsNullOrEmpty(spojitko))
				spojitko = "AND";
			else
				spojitko = spojitko.Trim().ToUpper();
			if (!(spojitko == "AND" || spojitko == "OR"))
				spojitko = "AND";
			return " " + spojitko + " ";
		}
		/// <summary>
		/// Vrátí typově přesný vztah podle hodnoty string načtené z definice (this.Relace)
		/// </summary>
		/// <param name="relace"></param>
		/// <returns></returns>
		private FilterRelType _GetRelaceEx(string relace)
		{
			if (String.IsNullOrEmpty(relace)) return FilterRelType.NotDefined;
			string rel = relace.ToUpper().Trim();
			switch (rel)
			{
				case "=": return FilterRelType.Equal;
				case "LIKE": return FilterRelType.Like;
				case "NOT LIKE": return FilterRelType.NotLike;
				case "<": return FilterRelType.LessThan;
				case ">": return FilterRelType.GreatThan;
				case "BETWEEN": return FilterRelType.Between;
				case "<>": return FilterRelType.NotEqual;
				case "<=": return FilterRelType.LessOrEqual;
				case ">=": return FilterRelType.GreatOrEqual;
				case "IS NOT NULL": return FilterRelType.IsNotNull;
				case "IS NULL": return FilterRelType.IsNull;
				case "NOT EXISTS": return FilterRelType.NotExists;
				case "IN": return FilterRelType.In;
				case "NOT IN": return FilterRelType.NotIn;
				case "IN2": return FilterRelType.In2;
				case "NOT IN2": return FilterRelType.NotIn2;
				case "LOCK": return FilterRelType.Lock;
			}
			return FilterRelType.NotDefined;
		}
		/// <summary>
		/// Vrátí datový typ.
		/// Pozor: pokud je uveden falešný vztah na pořadač, vrací se hodnota FilterDataType.FolderChar.
		/// </summary>
		/// <returns></returns>
		private FilterDataType _GetDatatypEx()
		{
			if (this.IsCharFolder)
				return FilterDataType.FolderChar;

			string dataTyp = (String.IsNullOrEmpty(this.Datatyp) ? "" : this.Datatyp.ToLower().Trim());
			switch (dataTyp)
			{
				case "boolean":
					return FilterDataType.Boolean;
				case "ck_saldo_typ":
					return FilterDataType.CkSaldoTyp;
				case "date":
					return FilterDataType.Date;
				case "datetime":
					return FilterDataType.DateTime;
				case "folder":
					return FilterDataType.Folder;
				case "kategorie":
					return FilterDataType.Kategorie;
				case "number":
					return FilterDataType.Number;
				case "pointer":
					return FilterDataType.Pointer;
				case "varchar":
					return FilterDataType.Varchar;
			}
			return FilterDataType.None;
		}
		/// <summary>
		/// Vrátí hodnotu filtračního kritéria (jako string) za tuto položku filtru.
		/// </summary>
		/// <returns></returns>
		private string _GetValueEx(string hodnotaVis)
		{
            string valueEx;
			switch (this.DatatypEx)
			{
				case FilterDataType.None:
				case FilterDataType.Boolean:
				case FilterDataType.CkSaldoTyp:
				case FilterDataType.Kategorie:
					return "";
				case FilterDataType.Date:
				case FilterDataType.DateTime:
					return "'" + _GetWithout(hodnotaVis, "'") + "'";
				case FilterDataType.Folder:
				case FilterDataType.Pointer:
					return (this.HodnotaPointer.IsNull ? "0" : this.HodnotaPointer.Value.ToString());
				case FilterDataType.FolderChar:
					return _GetOnly(hodnotaVis, "0123456789");
				case FilterDataType.Number:
                    valueEx = _GetOnly(hodnotaVis.Replace(",", "."), "-0123456789.");
                    if (String.IsNullOrEmpty(valueEx))
                        return "0";
                    return valueEx;
				case FilterDataType.Varchar:
					return SqlFormat.ToSql(hodnotaVis);
			}
			return "";
		}
		/// <summary>
		/// Vrátí z daného text takový text, v němž z původního textu zůstaly jen povolené znaky.
		/// Vrací tedy jen to, co odpovídá filtru.
		/// </summary>
		/// <param name="expression">Původní text</param>
		/// <param name="only">Povolené znaky</param>
		/// <returns></returns>
		private string _GetOnly(string expression, string only)
		{
			if (expression == null || only == null || only.Length == 0) return "";
			string result = "";
			foreach (char e in expression)
			{
				if (only.Contains(e))
					result += e.ToString();
			}
			return result;
		}
		/// <summary>
		/// Vrátí z daného text takový text, kde z původního textu byly odebrány dané znaky.
		/// Vrací tedy to, co je v textu navíc.
		/// </summary>
		/// <param name="expression">Původní text</param>
		/// <param name="without">Odebírané znaky</param>
		/// <returns></returns>
		private string _GetWithout(string expression, string without)
		{
			if (expression == null || without == null || without.Length == 0) return "";
			string result = "";
			foreach (char e in expression)
			{
				if (!without.Contains(e))
					result += e.ToString();
			}
			return result;
		}
		#endregion
		#region PODPORA INTERAKTIVNÍ MODIFIKACE
		/// <summary>
		/// Vytvoří a vrátí novou položku filtru podle daných parametrů
		/// </summary>
		/// <param name="entityNumber"></param>
		/// <param name="fullDbColumn"></param>
		/// <param name="filterExpression"></param>
		/// <param name="spojitko"></param>
		/// <returns></returns>
		internal static FilterItemCls CreateForExpression(FilterInfo parent, int entityNumber, string fullDbColumn, string filterExpression, string spojitko)
		{
			FilterItemCls item = new FilterItemCls();
			item.Parent = parent;
			item._entita = new System.Data.SqlTypes.SqlInt16((Int16)entityNumber);
			item._column_real = fullDbColumn;
			item._expression = filterExpression;
			item._datatyp = "boolean";
			item._spojitko = spojitko;
			return item;
		}
		/// <summary>
		/// Přidá otevírací závorku doleva (do this.LeftBraces)
		/// </summary>
		internal void ParenthesiseLeft()
		{
			this._left_braces = (String.IsNullOrEmpty(this._left_braces) ? "" : this._left_braces.Trim()) + "(";
		}
		/// <summary>
		/// Přidá zavírací závorku doprava (do this.RightBraces)
		/// </summary>
		internal void ParenthesiseRight()
		{
			this._right_braces = (String.IsNullOrEmpty(this._right_braces) ? "" : this._right_braces.Trim()) + ")";
		}
		/// <summary>
		/// Do tohoto prvku vepíše text spojítka
		/// </summary>
		/// <param name="spojitko"></param>
		internal void SetSpojitko(string spojitko)
		{
			this._spojitko = spojitko;
		}
		#endregion
	}
	#endregion
	#region CLASS FilterEntityCls : Jedna entita (jedna třída použitá ve stromu tříd)
	/// <summary>
	/// FilterEntityCls : Tato tabulka (lcs.filter_painter) není navázaná k žádné Noris třídě.
	/// Data načtena z tabulky lcs.filter_painter
	/// </summary>
	public partial class FilterEntityCls : Noris.Schedule.Support.Sql.IDbRecord, INorisClassTreeEntity
	{
		/// <summary>
		/// Vytvoří a vrátí root entitu pro danou třídu.
		/// </summary>
		/// <param name="classNumber"></param>
		/// <returns></returns>
		public static FilterEntityCls CreateRootForClass(int classNumber)
		{
			FilterEntityCls entity = new FilterEntityCls();
			entity._cislo_entity = new System.Data.SqlTypes.SqlInt16(1);
			entity._cislo_filtru = 0;
			entity._cislo_poradace = System.Data.SqlTypes.SqlInt16.Null;
			entity._cislo_tridy = classNumber;
			entity._cislo_vztahu = 0;
			entity._join_type = null;
			entity._nazev_entity = "";
			entity._parent_entita = System.Data.SqlTypes.SqlInt16.Null;
			entity._pk = 0;
			entity._pocet_subentit = System.Data.SqlTypes.SqlInt16.Null;
			entity._strana = null;
			entity._type = 0;
			entity._x = new System.Data.SqlTypes.SqlInt16(0);
			entity._y = new System.Data.SqlTypes.SqlInt16(0);
			return entity;
		}
	}
	#endregion
	#endregion
	#region ENUM pro filtry
	/// <summary>
	/// Typy dat v položce filtru
	/// </summary>
	public enum FilterDataType
	{
		None,
		Boolean,
		CkSaldoTyp,
		Date,
		DateTime,
		Folder,
		FolderChar,
		Kategorie,
		Number,
		Pointer,
		Varchar
	}
	/// <summary>
	/// Typy základních vztahů v polžoce filtru = vztah mezi datovou hodnotou a filtračním zadáním (rovno, nerovno, menší, zadáno, ...)
	/// </summary>
	public enum FilterRelType
	{
		/// <summary>
		/// Není
		/// </summary>
		NotDefined,
		/// <summary>
		/// =            (povoleno pro vztahy)
		/// </summary>
		Equal,
		/// <summary>
		/// LIKE         (povoleno jen pro varchar)
		/// </summary>
		Like,
		/// <summary>
		/// NOT LIKE     (povoleno jen pro varchar)
		/// </summary>
		NotLike,
		/// <summary>
		/// &lt;         (Menší než)
		/// </summary>
		LessThan,
		/// <summary>
		/// &gt;         (Větší než)
		/// </summary>
		GreatThan,
		/// <summary>
		/// BETWEEN      (Jen pro čísla)
		/// </summary>
		Between,
		/// <summary>
		/// &lt;&gt;     (Není rovno)
		/// </summary>
		NotEqual,
		/// <summary>
		/// &lt;=        (Menší nebo rovno) 
		/// </summary>
		LessOrEqual,
		/// <summary>
		/// &gt;=        (Větší nebo rovno) 
		/// </summary>
		GreatOrEqual,
		/// <summary>
		/// IS NOT NULL  "Zadáno"    (povoleno i pro vztahy)
		/// </summary>
		IsNotNull,
		/// <summary>
		/// IS NULL      "Nezadáno"  (povoleno i pro vztahy)
		/// </summary>
		IsNull,
		/// <summary>
		/// NOT EXISTS   "Nerozlišeno"
		/// </summary>
		NotExists,
		/// <summary>
		/// IN           (hodnota_pointer odkazuje na skupinu čísel, která je uložena asi v tabulce ...)
		/// </summary>
		In,
		/// <summary>
		/// NOT IN       (hodnota_pointer odkazuje na skupinu čísel, která je uložena asi v tabulce ...)
		/// </summary>
		NotIn,
		/// <summary>
		/// IN2          (hodnota_vis obsahuje přímo seznam povolených hodnot)
		/// </summary>
		In2,
		/// <summary>
		/// NOT IN2      (hodnota_vis obsahuje přímo seznam zakázaných hodnot)
		/// </summary>
		NotIn2,
		/// <summary>
		/// LOCK         (výraz obsahuje zámek, hodnota_vis obsahuje text zámku, hodnota_vis2 obsahuje parametry aplikace zámku pro otevření dialogového okna zámku)
		/// </summary>
		Lock
	}
	#endregion

	/*
	 
-- Šablona:
declare @cn int;
select @cn = 11206

-- Přehled šablon, základ pro uživatele
SELECT * FROM lcs.template where cislo_template = @cn;

-- Další data šablony, 1 řádek
SELECT * FROM lcs.noris_gs_template where cislo_sablony = @cn;

-- Položky šablony:
SELECT * FROM lcs.noris_gs_main where cislo_sablony = @cn ORDER BY sort

-- Barevné označení:
SELECT * FROM lcs.noris_gs_select where cislo_sablony = @cn ORDER BY sort

-- Barevné označení:
SELECT * FROM lcs.noris_gs_sum_print where cislo_sablony = @cn -- ORDER BY 


--SELECT * FROM lcs.pohledy


SELECT o.name, c.name 
FROM sys.columns c
join sys.objects o on o.object_id = c.object_id and o.type = 'U'
where c.name like '%templ%' or c.name like '%sablo%'
order by o.name, c.name

	 
	 
	 */
}
